package com.hospital.register.order.service.impl;

import com.github.wxpay.sdk.WXPayUtil;
import com.hospital.register.common.exception.BusinessException;
import com.hospital.register.common.helper.HttpRequestHelper;
import com.hospital.register.common.result.Result;
import com.hospital.register.common.result.ResultCodeEnum;
import com.hospital.register.core.client.HospitalFeignClient;
import com.hospital.register.enums.OrderStatusEnum;
import com.hospital.register.enums.PaymentStatusEnum;
import com.hospital.register.enums.PaymentTypeEnum;
import com.hospital.register.model.order.PaymentInfo;
import com.hospital.register.model.order.RefundInfo;
import com.hospital.register.order.service.OrderInfoService;
import com.hospital.register.order.service.PaymentInfoService;
import com.hospital.register.order.service.WxpayService;
import com.hospital.register.order.utils.HttpClient;
import com.hospital.register.order.utils.WXPayAccountConstants;
import com.hospital.register.order.utils.WxpayHttpClient;
import com.hospital.register.vo.core.api.HospitalSignVo;
import com.hospital.register.vo.order.OrderPayQrDto;
import com.hospital.register.vo.order.OrderQueryVo;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
public class WxpayServiceImpl implements WxpayService {

    /**
     * 处理订单业务
     */
    @Autowired
    private OrderInfoService orderInfoService;

    /**
     * 处理支付记录
     */
    @Autowired
    private PaymentInfoService paymentInfoService;

    /**
     * 访问 Redis
     */
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 访问医院服务
     */
    @Autowired
    private HospitalFeignClient hospitalFeignClient;

    /**
     * 记录日志
     */
    private final Logger logger = LoggerFactory.getLogger(WxpayServiceImpl.class);

    /**
     * 订单支付二维码在缓存中 key 的的前缀
     */
    private final String ORDER_PAY_QR_PREFIX = "hospital:order:pay:qr";

    @Override
    public OrderPayQrDto willPayment(Long orderId) {
        // 查询缓存
        OrderPayQrDto qr = (OrderPayQrDto) redisTemplate.opsForValue()
                .get(this.ORDER_PAY_QR_PREFIX + orderId.toString());
        if (qr != null) {
            return qr;
        }

        // 查询订单
        OrderQueryVo order = this.orderInfoService.selectById(orderId);
        if (order == null) {
            throw new BusinessException("订单不存在", ResultCodeEnum.PARAM_ERROR.getCode());
        }

        // 如果已超过退号时间，则不能支付
        if (System.currentTimeMillis() > order.getQuitTime().getTime()) {
            throw new BusinessException("已超过退号时间，不能支付", ResultCodeEnum.PARAM_ERROR.getCode());
        }

        // 如果订单不是待支付状态，则不能支付
        if (!OrderStatusEnum.UNPAID.getStatus().equals(order.getOrderStatus())) {
            throw new BusinessException("订单已支付或已取消，不可支付", ResultCodeEnum.PARAM_ERROR.getCode());
        }

        // 调用微信，生成二维码
        qr = this.willPayment(order);
        if (qr.getResultCode().equals("SUCCESS")) {
            redisTemplate.opsForValue().set(order.getId().toString(), qr, 120, TimeUnit.MINUTES);
            // 新增支付记录
            this.paymentInfoService.insert(order, PaymentTypeEnum.WEIXIN.getStatus());
        }
        return qr;
    }

    @Transactional
    @Override
    public Result checkPayStatus(Long orderId) {
        // 根据orderId获取订单信息
        OrderQueryVo order = orderInfoService.selectById(orderId);
        if (order == null) {
            throw new BusinessException("订单查询失败", ResultCodeEnum.SERVICE_ERROR.getCode());
        }

        Map<String, String> resultMap = this.queryPayStatus(order.getOutTradeNo());
        if (resultMap == null) {
            return Result.fail().message("支付出错");
        } else if (!"SUCCESS".equals(resultMap.get("trade_state"))) {
            return Result.ok().message("支付中");
        }

        // 支付成功，更新订单和支付状态
        Date currentTime = new Date();
        String out_trade_no = resultMap.get("out_trade_no");// 订单编码
        Long paymentId = this.paymentInfoService.selectIdByConditions(orderId, PaymentTypeEnum.WEIXIN.getStatus(),
                null);
        // 即使当前记录不是待支付状态，也更新，因为钱已经收到了。
        if (paymentId == null) {
            throw new BusinessException("找不到本地支付记录", ResultCodeEnum.PARAM_ERROR.getCode());
        }

        PaymentInfo newPaymentInfo = new PaymentInfo();
        newPaymentInfo.setId(paymentId);
        newPaymentInfo.setPaymentStatus(PaymentStatusEnum.PAID.getStatus());
        newPaymentInfo.setTradeNo(resultMap.get("transaction_id"));
        newPaymentInfo.setCallbackTime(currentTime);
        newPaymentInfo.setCallbackContent(resultMap.toString());
        newPaymentInfo.setUpdateTime(currentTime);
        this.paymentInfoService.updateById(newPaymentInfo);

        // 更新订单状态
        boolean updatedOrder = this.orderInfoService.updateStatusById(orderId, OrderStatusEnum.UNPAID.getStatus(),
                OrderStatusEnum.PAID.getStatus(), currentTime);
        if (!updatedOrder) {
            throw new BusinessException("订单数据异常，请联系管理员，可能是多平台重复支付，或该订单已不可用", ResultCodeEnum.PARAM_ERROR.getCode());
        }

        // 调用医院接口，更新订单支付信息
        HospitalSignVo sign = this.hospitalFeignClient.getSign(order.getHoscode());
        if (sign == null) {
            throw new BusinessException("医院信息查询失败", ResultCodeEnum.DATA_ERROR.getCode());
            // 即使医院锁定也允许查询订单支付信息
        }

        this.logger.info("支付成功，通知医院更新订单状态。");
        Map<String, Object> reqMap = new HashMap<>(8);
        reqMap.put("hoscode", order.getHoscode());
        reqMap.put("hosRecordId", order.getHosRecordId());

        Result<Object> result = HttpRequestHelper.sendToHospital(reqMap, sign.getApiUrl() + "/order/updatePayStatus",
                sign.getSignKey(), Object.class);
        return Result.ok().message("支付成功");
    }

    @Override
    public Map<String, String> refund(RefundInfo refundInfo) {
        // 首先验证证书配置
        if (!validateCertConfig()) {
            throw new BusinessException("微信支付证书配置验证失败，请检查证书文件和配置", ResultCodeEnum.SERVICE_ERROR.getCode());
        }
        
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("transaction_id", refundInfo.getTradeNo()); // 微信订单号
        paramMap.put("out_trade_no", refundInfo.getOutTradeNo()); // 商户订单编号
        paramMap.put("out_refund_no", "tk" + refundInfo.getOutTradeNo()); // 商户退款单号
        paramMap.put("total_fee", "1");
        paramMap.put("refund_fee", "1");

        // paramMap.put("total_fee", paymentInfo.getTotalAmount().multiply(new
        // BigDecimal("100")).longValue() + "");
        // paramMap.put("refund_fee",paymentInfo.getTotalAmount().multiply(new
        // BigDecimal("100")).longValue()+"");

        this.logger.info("微信退款请求参数：" + paramMap);
        try {
            // 首先尝试使用新的微信支付客户端
            this.logger.info("尝试使用微信官方SDK客户端发起退款请求...");
            Map<String, String> result = WxpayHttpClient.refund(paramMap);
            this.logger.info("微信退款响应结果：" + result);
            return result;
        } catch (Exception e) {
            this.logger.warn("微信官方SDK客户端退款失败，尝试使用原有实现：" + e.getMessage());
            
            // 如果新客户端失败，尝试使用原有实现
            try {
                Map<String, String> result = this.callWxpay("/secapi/pay/refund", paramMap, true);
                this.logger.info("原有实现退款响应结果：" + result);
                return result;
            } catch (Exception e2) {
                this.logger.error("两种退款方式都失败，错误信息：" + e2.getMessage(), e2);
                // 检查是否是SSL证书相关错误
                if (e2.getMessage() != null && e2.getMessage().contains("SSL")) {
                    this.logger.error("SSL证书错误，请检查证书文件路径和密码配置");
                    this.logger.error("证书路径：" + WXPayAccountConstants.CERT);
                    this.logger.error("商户号：" + WXPayAccountConstants.PARTNER);
                }
                throw new BusinessException("微信退款服务异常：" + e2.getMessage(), ResultCodeEnum.SERVICE_ERROR.getCode());
            }
        }
    }
    
    /**
     * 验证证书配置是否正确
     * @return true=证书配置正确，false=证书配置有问题
     */
    private boolean validateCertConfig() {
        try {
            this.logger.info("开始验证微信支付证书配置...");
            
            // 检查证书文件是否存在
            java.io.File certFile = new java.io.File(WXPayAccountConstants.CERT);
            if (!certFile.exists()) {
                this.logger.error("证书文件不存在：" + WXPayAccountConstants.CERT);
                return false;
            }
            
            if (!certFile.canRead()) {
                this.logger.error("证书文件不可读：" + WXPayAccountConstants.CERT);
                return false;
            }
            
            this.logger.info("证书文件验证通过，路径：" + WXPayAccountConstants.CERT);
            this.logger.info("证书文件大小：" + certFile.length() + " bytes");
            this.logger.info("商户号（证书密码）：" + WXPayAccountConstants.PARTNER);
            
            return true;
        } catch (Exception e) {
            this.logger.error("证书配置验证失败：" + e.getMessage(), e);
            return false;
        }
    }

    /**
     * 调用微信生成订单支付二维码
     *
     * @param order 订单信息
     * @return 订单支付二维码
     */
    private OrderPayQrDto willPayment(OrderQueryVo order) {
        // 拼接正文
        StringBuilder bodyBuilder = new StringBuilder("您将预约")
                .append(new DateTime(order.getReserveDate()).toString("yyyy-MM-dd")).append(" ")
                .append(order.getParam().getOrderStatusString()).append("，")
                .append(order.getHosname()).append("医院 ")
                .append(order.getDepname()).append(" ")
                .append(order.getTitle()).append(" 就诊");

        // 把参数转换xml格式，使用商户key进行加密
        Map paramMap = new HashMap(16);
        paramMap.put("body", bodyBuilder.toString());
        paramMap.put("out_trade_no", order.getOutTradeNo());
        // paramMap.put("total_fee", order.getAmount().multiply(new
        // BigDecimal("100")).longValue()+"");
        paramMap.put("total_fee", "1"); // 为了测试，统一写成这个值
        paramMap.put("spbill_create_ip", "127.0.0.1");
        paramMap.put("notify_url", "http://guli.shop/api/order/weixinPay/weixinNotify");
        paramMap.put("trade_type", "NATIVE");
        this.logger.info("微信支付 生成二维码，参数：" + paramMap);

        // 调用微信生成二维码接口,httpclient调用
        Map<String, String> resultMap = this.callWxpay("/pay/unifiedorder", paramMap);

        // 封装返回结果
        OrderPayQrDto payQr = new OrderPayQrDto();
        payQr.setOrderId(order.getId());
        payQr.setTotalFee(order.getAmount());
        payQr.setResultCode(resultMap.get("result_code"));
        payQr.setCodeUrl(resultMap.get("code_url")); // 二维码地址
        this.logger.info("二维码地址" + payQr.getCodeUrl());

        return payQr;
    }

    /**
     * 调用微信 查询订单的支付状态
     *
     * @param outTradeNo 订单交易号
     * @return 支付状态
     */
    private Map<String, String> queryPayStatus(String outTradeNo) {
        // 封装提交参数
        Map paramMap = new HashMap(8);
        paramMap.put("out_trade_no", outTradeNo);
        this.logger.info("微信支付 查询支付状态，参数：" + paramMap);

        return this.callWxpay("/pay/orderquery", paramMap);
    }

    /**
     * 向微信支付发起请求
     *
     * @param subUri     uri
     *                   自动添加前缀：https://api.mch.weixin.qq.com
     * @param parameters 参数列表
     * @param withCert   是否采用证书
     * @return 响应结果
     */
    private Map<String, String> callWxpay(String subUri, Map<String, String> parameters, boolean withCert) {
        // 填充公众号 id、商户编号、随机字符串
        parameters.put("appid", WXPayAccountConstants.APPID); // 公众号ID
        parameters.put("mch_id", WXPayAccountConstants.PARTNER); // 商户编号
        parameters.put("nonce_str", WXPayUtil.generateNonceStr());// 随机字符串

        try {
            HttpClient client = new HttpClient("https://api.mch.weixin.qq.com" + subUri);
            client.setXmlParam(WXPayUtil.generateSignedXml(parameters, WXPayAccountConstants.PARTNERKEY));
            client.setHttps(true);
            if (withCert) {
                // 设置证书
                client.setCert(true);
                client.setCertPassword(WXPayAccountConstants.PARTNER);
            }
            client.post();

            // 得到微信接口返回数据
            String xml = client.getContent();
            //日志输出微信接口返回数据
            this.logger.info("微信支付接口返回 xml 数据：" + xml);
            if (xml == null || xml.isEmpty()) {
                this.logger.error("调用微信支付响应为空，xml 响应：" + xml);
                throw new BusinessException("微信支付响应结果为空", ResultCodeEnum.SERVICE_ERROR.getCode());
            }

            Map<String, String> resultMap = WXPayUtil.xmlToMap(xml);
            if (resultMap == null || resultMap.isEmpty()) {
                this.logger.error("微信支付响应结果转换失败，响应 xml：" + xml);
                throw new BusinessException("微信支付响应结果转换失败", ResultCodeEnum.SERVICE_ERROR.getCode());
            } else {
                this.logger.info("微信支付响应结果" + resultMap);
            }
            return resultMap;
        } catch (Exception e) {
            this.logger.info(e.toString());
            throw new BusinessException("调用微信支付失败", ResultCodeEnum.SERVICE_ERROR.getCode());
        }
    }

    /**
     * 向微信支付发起请求，不携带证书
     *
     * @param subUri     uri
     *                   自动添加前缀：https://api.mch.weixin.qq.com
     * @param parameters 参数列表
     * @return 响应结果
     */
    private Map<String, String> callWxpay(String subUri, Map<String, String> parameters) {
        return this.callWxpay(subUri, parameters, false);
    }

}
